
/**
 * 消息提示插件
 * $(xxx).msger(msg);
 * $(xxx).msger(msg, position);
 * msg 消息内容
 * position 位置:left|right|top|bottom
 */
(function($) {
	var INIT = "WARNING-INIT";
	var OPTIONS = {
		placement : "auto top",
		trigger : "manual"
	};
	$.fn.msger = function(msg, position) {
		return this.each(function() {
			var me = $(this);
			var popover = me.data('bs.popover');
			if (!popover) {
				me.popover($.extend({}, OPTIONS, { container:'body', content:msg, placement:position }));
				popover = me.data('bs.popover');
				me.off('focusout.bs.popover').on('focusout.bs.popover', $.proxy(popover.leave, popover));
			} else {
				popover.options.content = msg;
				if (position) { popover.options.placement = position; }
			}
			if (me.is("a") && !me.attr("href")) { // 没有href的a无法获得焦点
				me.attr("href", "javascript:void(0)");
			}
			me.focus();
			popover.enter(popover);
		});
	};
})(jQuery);


/**
 * 全局消息提示
 * $.msger(msg); // msg 消息内容
 * $.msger(options);
	options = {
		autoclose: 4000, // 自动关闭的时间, 0为不自动关闭
		closable : true, // 是否显示关闭按钮
		group : "DEFAULT", // 同一组同时只会显示一个, false=不分组
		style : "success", // success|info|warning|danger
		title : undefined,
		content : undefined,
		buttons : [] // [ { style:default|primary|success|info|warning|danger, text:'确定', fn:function } ]
	};
 */
(function($) {
	var template = 
	'	<div class="x-msger" <#if(this.group){#>data-group="<#=this.group#>"><#}#>' +
	'		<div class="alert alert-<#=this.style || "success"#>" role="alert">' +
	'			<#if(this.closable){#><button type="button" class="close"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button><#}#>' +
	'			<#if(this.title){#><h4 class="msg-title"><#=this.title#></h4><#}#>' +
	'			<p class="msg-content"><#=this.content || "content is undefined."#></p>' +
	'			<#if(this.buttons && this.buttons.length){#>' +
	'			<p class="msg-buttons">' +
	'				<#$.each(this.buttons, function(i, btn) {#>' +
	'				&nbsp;<button type="button" class="btn btn-<#=this.style || "default"#>"><#=this.text || "button text is undefined"#></button>' +
	'				<#});#>' +
	'			</p>' +
	'			<#}#>' +
	'		</div>' +
	'	</div>';
	$.msger = function(options) {
		if (typeof (options) == "string") { options = { content:options }; }

		options = $.extend({}, o.options, options);

		if (!o.generate) { o.generate = baidu.template(o.template); }
		// 先删除相同group的消息框
		if (options.group) {
			var selector = ".x-msger[data-group='" + options.group + "']";
			$(document.body).children(selector).each(function() {
				remove.call(this);
			});
		}

		var html = o.generate(options);
		var msger = $(html);
		msger
		.appendTo(document.body)
		.data(o.keys.OPTIONS, options)
		// 关闭按钮
		.find(".close")
		.on("click.close.msger", $.proxy(close, msger[0]));
		// 显示
		show.call(msger[0]);

		// 自动关闭
		if (options.autoclose > 0) {
			options.timer = setTimeout(function() {
				close.call(msger[0]);
			}, options.autoclose);
		}

		// 绑定按钮的点击事件
		if (options.buttons && options.buttons.length) {
			msger.find(".msg-buttons .btn").each(function(i) {
				var btn = options.buttons[i];
				if ($.isFunction(btn.fn)) {
					$(this).click(btn.fn);
				}
			});
		}
	};
	var show = function(e) {
		if (e) { e.preventDefault(); }
		var msger = $(this);

		msger.css('top', '-100%').show()
			.css('top', -msger.height())
			.animate({ top:0 });
	};
	var close = function(e) {
		if (e) { e.preventDefault(); }
		var msger = $(this);

		// 关闭前触发close事件
		msger.trigger(e = $.Event('close.msger'));
		// 一旦被阻止就不关闭了
		if (e.isDefaultPrevented()) { return; }

		msger.animate({ top: -msger.height() }, function() {
			remove.call(this);
		});
	};
	var remove = function() {
		// 分离, 触发事件, 移除数据
		$(this).detach().trigger('closed.msger').remove();
	};
	var o = $.msger.defaults = {
		TRANSITION_DURATION: 300,
		template: template,
		keys: { 
			OPTIONS:"MSGER_OPTIONS"
		},
		options: {
			group : "DEFAULT",
			style : "warning",
			autoclose: 5000,
			closable : true
		}
	};
})(jQuery);




+function() {
	/**
	 * -----------------------------------------------------------
	 * 填充内容: 通过url下载数据, 通过模板生成html, 插入到指定位置
	 * $(".box").fillContent(options);
	 * $(".box").fillContent(function); // function = options.succ
	 * -----------------------------------------------------------
		<div>
			<div class="box"
					data-fill="url:'actions/xxx/list.json',
						clear:'.data-row',template:'data-rows',appendTo:'this',
						panel:'parent',loadingElement:'.data-loading',notFountElement:'.data-not-found'">
				<div class="title-row"> 标题行 </div>
				<div class="data-row"> 数据行1 </div>
				<div class="data-row"> 数据行2 </div>
			</div>
			<div class="data-loading"> 加载中 </div>
			<div class="data-not-found hide"> 暂无数据 </div>
		</div>
		<script type="data-rows">
		</script>
	 * -----------------------------------------------------------
	 * options: {
		+ url:'xxx/yyy.do' or url:{GET:'xxx/yyy.do'} or url:{POST:'xxx/yyy.do'}
		fixedParams: {}
		+ template:'data-rows'
		+ appendTo:'this' or prependTo:'.content-box'
		clearElement:'.data-row'
		panel:'parent', 
		loadingElement:'.data-loading' or loadingClass:'show-loading'
		notFountElement:'.data-not-found' or notFoundClass:'show-not-fount'
		data:'body' or function
		succ:function, fail:function, succTips:function, failTips:function
	 * }
	 * loadingElement/notFountElement: 通过panel找到element, 删除或添加hideClass实现显示和隐藏
	 * loadingClass/notFoundClass: 通过在panel上添加或删除对应class, 由css实现显示和隐藏
	 * -----------------------------------------------------------
	 * author: 赵卉华 / 2018-07-12
	 * -----------------------------------------------------------
	 */
	$.fn.fillContent = function(options, fn) {
		if ($.isFunction(options)) { options = { succ:options }; }
		else if ($.isFunction(fn)) { options.succ = options.succ || fn; }
	    return this.each(function() {
	    	var self = this;
	    	var me = $(self);
	    	var key = options && options.attr || v.options.attr || "fill";
	    	var o = $.extend(true, {}, v.options, me.zoptions(key), options);
    		var panel = v.fn.findPanel(me, o.panel);
	    	if (o.notFoundElement) {
	    		v.fn.hideTarget(panel, o.notFoundElement, o.hideClass);
	    	} else if (o.notFoundClass) {
	    		v.fn.removeClass(panel, o.notFoundClass);
	    	}
	    	$.zajax(o.url, o.fixedParams, {
		    	fail: o.fail,
		    	succTips: o.succTips || true,
		    	failTips: o.failTips || true,
	    		loading: {
	    			show: function() {
	    				if (o.loadingElement) {
		    				v.fn.showTarget(panel, o.loadingElement, o.hideClass);
	    				} else if (o.loadingClass) {
		    				v.fn.addClass(panel, o.loadingClass);
	    				}
	    			},
	    			stop: function() {
	    				if (o.loadingElement) {
		    				v.fn.hideTarget(panel, o.loadingElement, o.hideClass);
	    				} else if (o.loadingClass) {
		    				v.fn.removeClass(panel, o.loadingClass);
	    				}
	    			}
	    		},
	    		succ: function(json) {
		    		var data = v.fn.getData(json, o.data);
		    		if (v.fn.isEmpty(data)) {
	    				var target = v.fn.findElement(me, o.appendTo);
	    				v.fn.clearElement(target, o.clearElement, o);
	    				if (o.notFoundElement) {
		    				v.fn.showTarget(panel, o.notFoundElement, o.hideClass);
	    				} else if (o.notFoundClass) {
		    				v.fn.addClass(panel, o.notFoundClass);
	    				}
		    		} else {
		    			var space = !o.space ? null : v.fn.findPanel(me, o.panel);
			    		var html = v.fn.generateHtml(data, o.template, space);
			    		v.fn.fillContent(me, html, o);
		    		}
		    		if ($.isFunction(o.succ)) {
		    			this.element = self;
		    			o.succ.call(this, json, o);
		    		}
		    	}
	    	});
	    });
	};
	var v = $.fn.fillContent.defaults = {
		options: { 
			attr: "fill", 
			data: "body",
			hideClass: "hide",
		},
		fn: {
			findPanel: function(element, selector) {
				if (!selector || selector == "this") return element;
				if (selector == "parent") return element.parent();
				if (selector == "root") return $(document);
				if (selector == "body") return $(document.body);
				return element.closest(selector);
			},
			findElement: function(element, selector) {
				if (selector == "this" || selector == true) return element;
				return element.find(selector);
			},
			getData: function(json, field) {
	    		return $.zhh.field(json, field || "body");
			},
			isEmpty: function(data) {
				return !data || !data.length || data.length <= 0;
			},
			generateHtml: function(data, template, space) {
				return Utils.baidu.template(template, data);
			},
			fillContent: function(element, html, o) {
				if (!o.appendTo && !o.prependTo) { o.appendTo = "this"; }
				if (o.appendTo) {
					var target = v.fn.findElement(element, o.appendTo);
					v.fn.clearElement(target, o.clearElement, o);
					target.append(html);
				} else if (o.prependTo) {
					var target = v.fn.findElement(element, o.prependTo);
					v.fn.clearElement(target, o.clearElement, o);
					target.prepend(html);
				}
			},
			clearElement: function(element, selector) {
				if (!selector) return;
				element.find(selector).remove();
			},
			addClass: function(element, className) {
				if (!className) return;
				element.addClass(className);
			},
			removeClass: function(element, className) {
				if (!className) return;
				element.removeClass(className);
			},
			showTarget: function(element, selector, hideClass) {
				if (!selector) return;
				var target = element.find(selector);
				v.fn.showElement(target, hideClass);
			},
			hideTarget: function(element, selector, hideClass) {
				if (!selector) return;
				var target = element.find(selector);
				v.fn.hideElement(target, hideClass);
			},
			showElement: function(element, hideClass) {
				element.removeClass(hideClass || "hide");
			},
			hideElement: function(element, hideClass) {
				element.addClass(hideClass || "hide");
			}
		}
	};
}();






+function() {
	/**
	 * -----------------------------------------------------------
	 * 地址选择控件
	 * $(".area-division-box").areaChoose("init");
	 * -----------------------------------------------------------
		<div class="col-md-8 control-input area-division-box">
			<input type="hidden" name="areaCode" />
			<input type="hidden" name="township" />
			<input class="form-control" type="text" name="areaText" />
			<div class="vld-tips"></div>
			<div class="area-division-panel active hide">
				<div class="border">
					<a class="close-icon"><span class="icon fa fa-times"></span></a>
					<div class="switch-panel">
						<div class="input-box">
							<input class="form-control" type="text" name="inputText" placeholder="输入街道、乡镇、小区或商圈名称" />
						</div>
						<ul class="addresses">
						</ul>
						<a class="switch-link plain">手动选择行政区划&nbsp;&gt;&gt;</a>
					</div>
					<div class="switch-panel hide">
						<div>
							<ul class="choose-title">
								<li class="province"><a data-text="color-warn">选择省份/自治区</a></li>
								<li class="city hide"><a class="color-warn">选择城市/地区</a></li>
								<li class="district hide"><a class="color-warn">选择区/县</a></li>
							</ul>
						</div>
						<div>
							<ul class="choose-items">
							</ul>
						</div>
						<a class="switch-link plain">搜索地址快速定位&nbsp;&gt;&gt;</a>
					</div>
					<div class="hide">
						<script type="address-template">
						<# $.each(this, function() { #>
							<li data-area-code="<#=this.areaCode#>" data-location="<#=this.location#>">
								<div> <#=this.placeName#> </div>
								<div class="color-weak"> <#=this.areaText#> <#=this.address#> </div>
								<div class="resolve vertical-middle"><a class="btn btn-default btn-sm">选择</a></div>
							</li>
						<# }); #>
						</script>
						<script type="choose-item-template">
						<# $.each(this, function() { #>
							<li data-area-code="<#=this.areaCode#>"><a><#=this.shortName||this.areaName#></a></li>
						<# }); #>
						</script>
					</div>
				</div>
			</div>
		</div>
	 * -----------------------------------------------------------
	 * options: {
		+ suggestionUrl:'...' // 根据用户输入的关键字给出地址提示
		+ townshipUrl:'...' // 根据经纬度查询乡镇街道级信息
		+ areaDivisionsUrl:'...' // 根据行政区划数据
		ipLocatingUrl:'...' // 根据IP地址定位
		fixedParams: {}
		suggestionParams / townshipParams / areaDivisionsParams / ipLocatingParams
		changed:function
	 * }
	 * -----------------------------------------------------------
	 * author: 赵卉华 / 2018-07-20
	 * -----------------------------------------------------------
	 */
	var eachTree = function(tree, fn) {
		var i = 0;
		if ($.dt.isNumber(fn)) { i = fn; fn = arguments[2]; }
		$.each(tree, function() {
			fn.call(this, i++, this);
			if (this.children) { eachTree(this.children, i, fn); }
		});
	};
	var ipLocation = null;
	var areas = null; // { list:null, map:null, roots:null };
	$.fn.areaChoose = function(options) {
		if ($.isFunction(options)) { options = { changed:options }; }
	    return this.each(function() {
	    	var self = this;
	    	var me = $(self);
    		var panel = me.find(".area-division-panel");
	    	var key = options && options.attr || v.options.attr || "options";
	    	var o = $.extend(true, {}, v.options, me.zoptions(key), options);
    		
	    	if (!areas) {
	    		areas = { list:null, map:null, roots:null };
	    		// 查询行政区划数据
		    	$.zajax(o.areaDivisionsUrl, $.extend(true, {}, o.fixedParams, o.areaDivisionsParams), {
		    		succTips: false,
		    		failTips: false,
    				loading: {
    					show: function() {
    						panel.find(".choose-items").html('<li class="data-loading"></li>');
    					},
    					stop: function() {
    						// panel.find(".addresses").find(".data-loading").remove();
    					}
    				},
		    		succ: function(json) {
		    			if (json && json.body && json.body.length > 0) {
				    		areas.list = json.body;
				    		areas.map = {};
				    		var tree = $.zhh.treeparse(json.body, { upgrade:true, tid:"areaCode", parent:"parentCode" });
				    		areas.roots = tree;
				    		eachTree(tree, function(i, v) {
				    			areas.map[v.areaCode] = v;
				    		});
				    		panel.template(".choose-items", o.chooseItemTemplate, areas.roots);
		    			} else {
		    				
		    			}
			    	},
			    	fail: function(json) {
			    		
			    	}
		    	});
	    	}
	    	if (!ipLocation && o.ipLocatingUrl) {
	    		ipLocation = {};
	    		// 根据IP地址定位
		    	$.zajax(o.ipLocatingUrl, $.extend(true, {}, o.fixedParams, o.ipLocatingParams), {
		    		succTips: false,
		    		failTips: false,
		    		succ: function(json) {
		    			if (json && json.body) {
				    		ipLocation = json.body;
		    			}
			    	}
		    	});
	    	}

	    	// 记录提示文本
	    	panel.find("ul.choose-title li a").each(function() {
	    		var me = $(this);
	    		me.attr("data-text", me.text());
	    	});
	    	// 绑定事件
    		me.find("input[name=areaText]").focus(function() {
    			$(this).blur();
    			panel.removeClass("hide").find(".input-box input:visible").focus();
    		});
    		panel.find(".switch-link").click(function() {
    			$(this).closest(".switch-panel").addClass("hide").siblings(".switch-panel").removeClass("hide");
    		});
    		panel.find(".close-icon").click(function() {
    			panel.addClass("hide");
    		});
    		// 地址输入提示
    		var lastKeyword = null;
    		var cacheKeywords = {};
    		var querySuggestResult = function(input, keyword) {
    			lastKeyword = keyword;
    	    	var params = $.extend(true, {}, o.fixedParams, o.suggestionParams, { keyword:keyword, location:ipLocation.location });
    			input.zajax(o.suggestionUrl, params, {
    				loading: {
    					show: function() {
    						panel.find(".addresses").html('<li class="data-loading"></li>');
    					},
    					stop: function() {
    						// panel.find(".addresses").find(".data-loading").remove();
    					}
    				},
    				readForm: false,	
    	    		succTips: false,
    	    		failTips: false,
    	    		succ: function(json) {
    	    			var result = json && json.body || [];
    	    			cacheKeywords[keyword] = result;
    	    			handleSuggestResult(result);
    	    			if (lastKeyword && keyword != lastKeyword) {
    	    				querySuggestResult(input, lastKeyword);
    	    			}
    		    	},
    		    	fail: function(json) {
    		    		var msg = json && json.message || "查询失败";
    		    		panel.find(".addresses").html('<li class="data-not-found"> <div class="content">' + msg + '</div> </li>');
    		    	}
    			});
    		};
    		var handleSuggestResult = function(result) {
    			if (result && result.length > 0) {
    				panel.template(".addresses", o.addressTemplate, result);
    			} else {
    				panel.find(".addresses").html('<li class="data-not-found"> <div class="content">没有找到这个地址</div> </li>');
    			}
    		};
    		// 搜索地址快速定位
    		panel.find("input[name=inputText]").zchanged(function(value) {
    			var input = $(this);
    			var keyword = input.val().trim();
    			if (!keyword) { return; }
    			var cachedResult = cacheKeywords[keyword];
    			if (cachedResult) {
    				handleSuggestResult(cachedResult);
    			} else {
    				querySuggestResult(input, keyword);
    			}
    		});
    		var findArea = function(areaCode, townshipName) {
				var area = areas.map[areaCode];
				var areaTexts = [];
				var tempCode = areaCode;
				var lastName = null;
				while (tempCode) {
					var area = areas.map[tempCode];
					if (!area || area.type == "COUNTRY") { break; }
					var areaText = area.shortName || area.areaName;
					if (lastName == null || lastName != areaText) {
						areaTexts.splice(0, 0, areaText);
					}
					tempCode = area.parentCode;
					lastName = areaText;
				}
				return { areaCode:areaCode, areaText:areaTexts.join(" "), townshipName:townshipName };
    		};
    		var chooseArea = function(area) {
    			var township = area.townshipName;
				me.find("input[name=areaCode]").val(area.areaCode);
				me.find("input[name=township]").val(township || "");
				me.find("input[name=areaText]").val(township ? area.areaText + " " + township : area.areaText);
				var close = true;
				if ($.isFunction(o.changed)) {
					close = o.changed.call(self, area);
				}
				if (close !== false) {
					panel.addClass("hide");
				}
    		};
    		// 选择地址
    		var disableAddress = function(li, message) {
    			li.addClass("disabled");
    			if (message) { li.find(".btn").removeClass("btn-default").addClass("btn-link color-warn").text(message); }
    		};
    		var handleAddressByCode = function(li) {
				var areaCode = li.attr("data-area-code");
				var area = findArea(areaCode);
				if (area) {
					chooseArea(area);
				} else {
					disableAddress(li, "地址无效");
				}
    		};
    		var handleAddressByLocation = function(li) {
    			var location = li.attr("data-location");
    	    	var params = $.extend(true, {}, o.fixedParams, o.townshipParams, { location:location });
    			$.zajax(o.townshipUrl, params, {
    				readForm: false,	
    	    		succTips: false,
    	    		failTips: false,
    				loading: {
    					show: function() {
    						li.addClass("data-loading");
    					},
    					stop: function() {
    						li.removeClass("data-loading");
    					}
    				},
    	    		succ: function(json) {
    	    			var details = json && json.body;
    	    			if (details) {
    	    				var area = findArea(details.areaCode, details.townshipName);
    	    				if (area) {
    	    					chooseArea(area);
    	    				} else {
    	    					disableAddress(li, "地址无效");
    	    				}
    	    			} else {
    	    				handleAddressByCode(li);
    	    			}
    		    	},
    		    	fail: function(json) {
	    				disableAddress(li, json && json.message || "地址无效");
    		    	}
    			});
    		};
    		panel.on("click", "ul.addresses li", function() {
    			// 选中一个地址后, 根据经纬度查询乡镇街道级信息
    			var li = $(this);
    			if (li.is(".disabled")) { return; }
    			if (li.attr("data-location")) {
    				handleAddressByLocation(li);
    			} else {
    				handleAddressByCode(li);
    			}
    		});
    		// 选择行政区划
    		panel.on("click", "ul.choose-items li", function() {
    			var li = $(this);
    			var ul = li.parent();
    			var areaCode = li.attr("data-area-code");
    			var area = areas.map[areaCode];
    			var title = panel.find("ul.choose-title li." + area.type.toLowerCase());
    			title.attr("data-area-code", area.areaCode).removeClass("color-warn hide");
    			title.find("a").text(li.text());

    			if (area.type == "DISTRICT" || !area.children || area.children.length == 0) {
    				chooseArea(findArea(areaCode));
    				return;
    			}

    			// 香港/澳门的子节点直接是区
    			var nextType = null;
    			$.each(area.children, function() {
    				nextType = this.type; return false;
    			});

    			title.nextAll("li." + nextType.toLowerCase()).removeClass("hide");
    			panel.template(".choose-items", o.chooseItemTemplate, area.children);

    			var nli = ul.find("li");
    			if (nli.length == 1) {
    				setTimeout(function() { nli.click(); }, 0);
    			}
    		});
    		// 点击已经选择的行政区划, 等于取消行政区划
    		panel.on("click", "ul.choose-title li", function() {
    			var li = $(this);
    			li.nextAll().addClass("hide")
    			.andSelf().each(function() {
    				var li = $(this);
        			li.removeAttr("data-area-code").addClass("color-warn");
        			var link = li.find("a");
        			link.text(link.attr("data-text"));
    			});
    			var prev = li.prev();
    			if (prev.length > 0) {
        			var areaCode = prev.attr("data-area-code");
        			var area = areas.map[areaCode];
        			panel.template(".choose-items", o.chooseItemTemplate, area.children);
    			} else {
        			panel.template(".choose-items", o.chooseItemTemplate, areas.roots);
    			}
    		});
	    });
	};
	var v = $.fn.areaChoose.defaults = {
		options: { 
			attr: "options", 
			chooseItemTemplate: "choose-item-template",
			addressTemplate: "address-template"
		}
	};
}();


